package com.gitee.qdbp.general.thirdpart.biz.trade.service;

import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.result.ResultCode;
import com.gitee.qdbp.general.thirdpart.api.alipay.model.AlipayPayee;
import com.gitee.qdbp.general.thirdpart.api.trade.model.OrderData;
import com.gitee.qdbp.general.thirdpart.api.trade.model.PaidNotify;
import com.gitee.qdbp.general.thirdpart.api.trade.model.PaidResponse;
import com.gitee.qdbp.general.thirdpart.api.trade.model.PrepayData;
import com.gitee.qdbp.general.thirdpart.api.trade.model.PrepayParams;
import com.gitee.qdbp.general.thirdpart.api.trade.model.PrepayResponse;
import com.gitee.qdbp.general.thirdpart.api.trade.service.IPaymentBusinessService;
import com.gitee.qdbp.general.thirdpart.api.trade.service.IPaymentByOrderTypeService;
import com.gitee.qdbp.general.thirdpart.api.wechat.model.WechatPayee;
import com.gitee.qdbp.general.trade.enums.PaySource;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 根据订单类型分发业务处理
 *
 * @author zhaohuihua
 * @version 180126
 */
public class PaymentDistributeService implements IPaymentBusinessService, ApplicationContextAware {

    /** 日志对象 **/
    private static final Logger log = LoggerFactory.getLogger(PaymentDistributeService.class);

    private ApplicationContext context;

    private Map<String, IPaymentByOrderTypeService> services;

    /** 初始化, 查找所有的IPaymentByOrderTypeService的子类, 注册为orderType的服务处理类 **/
    @PostConstruct
    protected void init() {
        this.services = new HashMap<>();
        // 查找所有的IPaymentByOrderTypeService的子类
        Map<String, IPaymentByOrderTypeService> list = context.getBeansOfType(IPaymentByOrderTypeService.class);
        // 逐一注册为orderType的服务处理类
        for (IPaymentByOrderTypeService item : list.values()) {
            String[] orderTypes = item.getAbleOrderType();
            for (String orderType : orderTypes) {
                if (services.containsKey(orderType)) { // 相同类型的orderType只能注册一个服务处理类
                    String newer = item.getClass().getSimpleName();
                    String older = services.get(orderType).getClass().getSimpleName();
                    log.warn("Ignore {}-{}, already register the type to {}.", orderType, newer, older);
                }
                services.put(orderType, item);
            }
        }
    }

    /** 根据orderType获取服务处理类 **/
    protected IPaymentByOrderTypeService getServiceByOrderType(String orderType) throws ServiceException {
        if (VerifyTools.isBlank(orderType)) {
            log.error("PaymentDistributeService orderType is required.");
            throw new ServiceException(ResultCode.SERVER_INNER_ERROR);
        }
        IPaymentByOrderTypeService service = services.get(orderType);
        if (service == null) {
            log.error("PaymentByOrderTypeService instance by OrderType[{}] not found", orderType);
            throw new ServiceException(ResultCode.SERVER_INNER_ERROR);
        }
        return service;
    }

    /** 获取收款人信息(支付前) **/
    @Override
    @Transactional(readOnly = true)
    public WechatPayee getWechatPayee(PrepayData params, PaySource paySource, WechatPayee defaults) throws ServiceException {
        String orderType = params.getOrderType();
        IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
        return service.getWechatPayee(params, paySource, defaults);
    }

    /** 获取收款人信息(支付后) **/
    @Override
    @Transactional(readOnly = true)
    public WechatPayee getWechatPayee(OrderData data, PaidNotify notify, WechatPayee defaults) throws ServiceException {
        String orderType = data.getOrderType();
        IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
        return service.getWechatPayee(data, notify, defaults);
    }

    /** 获取收款人信息(支付前) **/
    @Override
    @Transactional(readOnly = true)
    public AlipayPayee getAlipayPayee(PrepayData params, PaySource paySource, AlipayPayee defaults) throws ServiceException {
        String orderType = params.getOrderType();
        IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
        return service.getAlipayPayee(params, paySource, defaults);
    }

    /** 获取收款人信息(支付后) **/
    @Override
    @Transactional(readOnly = true)
    public AlipayPayee getAlipayPayee(OrderData data, PaidNotify notify, AlipayPayee defaults) throws ServiceException {
        String orderType = data.getOrderType();
        IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
        return service.getAlipayPayee(data, notify, defaults);
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(readOnly = true)
    public String generatePaymentId(PrepayData data) throws ServiceException {
        String orderType = data.getOrderType();
        IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
        return service.generatePaymentId(data);
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(readOnly = true)
    public PrepayData onPrepayCheckOrder(PrepayParams params) throws ServiceException {
        String orderType = params.getOrderType();
        String lock = orderType + ":" + VerifyTools.nvl(params.getOrderId(), params.getDataId());
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            return service.onPrepayCheckOrder(params);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(readOnly = true)
    public void onPrepayCheckBusiness(PrepayData data) throws ServiceException {
        String orderType = data.getOrderType();
        String lock = orderType + ":" + data.getOrderId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            service.onPrepayCheckBusiness(data);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void onPrepayHandleOrder(PrepayData data, PrepayResponse response) throws ServiceException {
        String orderType = data.getOrderType();
        String lock = orderType + ":" + data.getOrderId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            service.onPrepayHandleOrder(data, response);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void onPrepayHandleBusiness(PrepayData data, PrepayResponse response) throws ServiceException {
        String orderType = data.getOrderType();
        String lock = orderType + ":" + data.getOrderId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            service.onPrepayHandleBusiness(data, response);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void onPrepayHandleError(PrepayData data, PrepayResponse response) throws ServiceException {
        String orderType = data.getOrderType();
        String lock = orderType + ":" + data.getOrderId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            service.onPrepayHandleError(data, response);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(readOnly = true)
    public OrderData onPaidQueryOrder(PaidNotify notify) throws ServiceException {
        String orderType = notify.getOrderType();
        String lock = orderType + ":" + notify.getPaymentId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            return service.onPaidQueryOrder(notify);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
    public boolean onPaidHandleOrder(OrderData data, PaidNotify notify, PaidResponse response) throws ServiceException {
        String orderType = notify.getOrderType();
        String lock = orderType + ":" + notify.getPaymentId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            return service.onPaidHandleOrder(data, notify, response);
        }
    }

    /** {@inheritDoc} **/
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
    public void onPaidHandleBusiness(OrderData data, PaidNotify notify, PaidResponse response) throws ServiceException {
        String orderType = notify.getOrderType();
        String lock = orderType + ":" + notify.getPaymentId();
        synchronized (lock.intern()) {
            IPaymentByOrderTypeService service = getServiceByOrderType(orderType);
            service.onPaidHandleBusiness(data, notify, response);
        }
    }

    /** {@inheritDoc} **/
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }

}
